package com.smile.cloud.admin.controller.client;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.smile.cloud.common.base.result.CommonResult;
import com.smile.cloud.redisConfig.distributedLock.redisson.AquiredLockWorker;
import com.smile.cloud.redisConfig.distributedLock.redisson.RedissonLocker;
import com.smile.cloud.redisConfig.service.RedisService;
import com.smile.cloud.zkConfig.distributedLock.zk.ZKAquiredLockWorker;
import com.smile.cloud.zkConfig.distributedLock.zk.ZookeeperLocker;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author smile
 * @version V1.0
 * @className RedisController
 * @description TODO
 * @date 2021/3/6
 **/
@Slf4j
@Api(value = "分布式锁")
@RestController
@RequestMapping("/v1/api/lock")
public class LockController {
    /**
     * 库存key
     */
    private final static String stockKey = "stock";

    @Autowired
    private RedisService redisService;
    @Autowired
    private RedissonLocker redissonLocker;
    @Autowired
    private ZookeeperLocker zookeeperLocker;

    /**
     * 初始化200个库存
     */
    @PostConstruct
    private void init() {
        redisService.set(stockKey, 100);
    }


    @GetMapping("/addStock/{num}")
    @ApiOperation("addStock")
    public CommonResult<Integer> redissonLock(@PathVariable("num") Integer num) throws Exception {
      /*  redisService.set(stockKey, 200);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 200; i++) {
            executorService.execute(new RedissonLockWorker(lockKey));
        }
        executorService.shutdown();*/
        redisService.set(stockKey, num);


        // 日活数量
        for (int i = 0; i < 1000; i++) {
            redisService.bitSet("online:2021-11-10", i, true);
        }


        Long aLong = redisService.bitCount("online:2021-11-10");
        log.info("2021-11-10日活数量：{}", aLong);

        // 文章点赞
        for (int i = 0; i < 1000; i++) {
            Boolean aBoolean = redisService.bitHasKey("like:note_1", i);
            if (aBoolean) {
                log.info("用户1 已点赞,取消点赞");
                redisService.bitSet("like:note_1", i, false);
            } else {
                redisService.bitSet("like:note_1", i, true);
            }
        }
        log.info("note_1文章点赞数量:{}", redisService.bitCount("like:note_1"));



        return CommonResult.success(null);

    }

    @GetMapping("/redisson")
    @ApiOperation("redisson锁")
    public CommonResult<Integer> redissonLock(@RequestParam("key") String lockKey) throws Exception {
        redisService.set(stockKey, 200);
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("redisson-pool-%d").build();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200, 60L, TimeUnit.SECONDS
                , new ArrayBlockingQueue<>(200), threadFactory);
//        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 200; i++) {
            executor.execute(new RedissonLockWorker(lockKey));
        }
        executor.shutdown();
        return CommonResult.success(1);
//        return DataResult.success(decreaseStock());
    }

    @GetMapping("/zk")
    @ApiOperation("zookeeper锁")
    public CommonResult<Integer> zookeeperLock(@RequestParam("key") String lockKey) throws Exception {
        redisService.set(stockKey, 200);
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("zk-pool-%d").build();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200, 60L, TimeUnit.SECONDS
                , new ArrayBlockingQueue<>(200), threadFactory);
//        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 200; i++) {
            executor.execute(new ZooKeeperLockWorker(lockKey));
        }
        executor.shutdown();
        return CommonResult.success(1);
//        return DataResult.success(decreaseStock());

    }


    class RedissonLockWorker implements Runnable {
        String lockKey;

        public RedissonLockWorker(String lockKey) {
            this.lockKey = lockKey;
        }

        @Override
        public void run() {
            try {
                redissonLocker.lock(lockKey, new AquiredLockWorker<Integer>() {
                    @Override
                    public Integer invokeAfterLockAquire() {
                        return decreaseStock();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    class ZooKeeperLockWorker implements Runnable {
        String lockKey;

        public ZooKeeperLockWorker(String lockKey) {
            this.lockKey = lockKey;
        }

        @Override
        public void run() {
            try {
                zookeeperLocker.lock(lockKey, new ZKAquiredLockWorker<Integer>() {
                    @Override
                    public Integer invokeAfterLockAquire() {
                        return decreaseStock();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 减少库存
     *
     * @return 库存数量
     */
    private Integer decreaseStock() {
        log.info(Thread.currentThread().getName() + " ---------- " + LocalDateTime.now());
        log.info(Thread.currentThread().getName() + " start");
        // 减库存
        Integer stocks = (Integer) redisService.get(stockKey);
        if (stocks > 0) {
            stocks--;
            redisService.set(stockKey, stocks);
            log.info("剩余库存：" + stocks);
        }
        log.info(Thread.currentThread().getName() + " end");
        log.info(Thread.currentThread().getName() + " ---------- " + LocalDateTime.now());
        return stocks;
    }
}
